package com.woniuxy.lab.woniu_lab.controller;


import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.woniuxy.lab.woniu_lab.commons.Result;
import com.woniuxy.lab.woniu_lab.exception.WebException;
import com.woniuxy.lab.woniu_lab.model.Range;
import com.woniuxy.lab.woniu_lab.model.User;
import com.woniuxy.lab.woniu_lab.service.ExperService;
import com.woniuxy.lab.woniu_lab.service.RangeService;
import com.woniuxy.lab.woniu_lab.service.UserService;
import com.woniuxy.lab.woniu_lab.service.dto.*;
import com.woniuxy.lab.woniu_lab.service.dto.rangeDto.GetRangeDto;
import com.woniuxy.lab.woniu_lab.service.dto.userDto.ExperRankingsDto;
import com.woniuxy.lab.woniu_lab.util.JwtUtil;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.constraints.*;
import java.util.List;

/**
 * <p>
 * 靶场表 前端控制器
 * </p>
 *
 * @author Silas
 * @since 2023-03-15
 */
@RestController
@RequestMapping("/range")
@Slf4j
@Api(tags = "[I]靶场与实验相关接口")
@Validated
public class RangeController {

    @Resource
    private RangeService rangeService;

    @Resource
    private UserService userService;

    @Resource
    private ExperService experService;

    //难度
    private static final String[] RANGE_LEVEL = {"low","middle","high"};
    private static final String[] RANGE_TYPE = {"扫描类","文件类","SQL注入类","XSS类",
            "CSRF类","SSRF类","代码执行","命令执行","文件上传类","文件包含类","反序列化类","Redis类","内网渗透类"};



    // 查询最新实验
    @ApiOperation("01 获取最新实验")
    @GetMapping("getNewExper")
    public Result<IPage<ExperDto>> getNewExper(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1") @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size
    ){
        log.info("获取最新实验 {} {}", page, size);
        IPage<ExperDto> newExper = rangeService.getNewExper(page, size);
        return Result.success(200, "SUCCESS", newExper);
    }


    /**
     * 根据难度查询靶场
     * @param page 页码
     * @param size 每页显示数
     * @param level 难度
     * @return IPage<RangeDto>
     */
    @ApiOperation("42 按难度查询CTF实验")
    @GetMapping("/queryRangeByLevel")
    public Result<IPage<RangeDto>> queryRangeByLevel(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1") @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size,
            @ApiParam(value = "难度 [low/middle/high]") @NotBlank(message = "难度不能为空")
            @Pattern(regexp = "low|middle|high",message = "难度参数传入错误") String level
    ){
        log.info("根据难度查询靶场:{}", level);
            IPage<RangeDto> pages =  rangeService.queryRangeByLevel(page, size, level);
            return Result.success(200, "根据难度查询靶场成功", pages);
    }

    /**
     * 根据分类条件查询靶场
     * @param page 页码
     * @param size 每页显示数
     * @param type 所属分类
     * @return IPage<RangeDto>
     */
    @ApiOperation("43 按类别查询CTF实验")
    @GetMapping("/queryRangeByType")
    public Result<IPage<RangeDto>> queryRangeByType(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1") @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size,
            @ApiParam(value = "类别 [扫描类/文件类/SQL注入类/XSS类/CSRF类/SSRF类/代码执行/命令执行/文件上传类/文件包含类/反序列化类/Redis类/内网渗透类]")
            @NotBlank(message = "类别不能为空") @Pattern(regexp = "扫描类|文件类|SQL注入类|XSS类|CSRF类|SSRF类|代码执行|命令执行|文件上传类|文件包含类|反序列化类|Redis类|内网渗透类"
            ,message = "类别参数传入错误") String type
    ){
        log.info("根据分类条件查询靶场:{}", type);
        IPage<RangeDto> pages =  rangeService.queryRangeByType(page, size, type);
        return Result.success(200, "根据分类条件查询靶场成功", pages);
    }

    /**
     * CTF积分排行榜
     * @return List<CtfLeaderBoardDto>
     */
    @ApiOperation("45 CTF实验得分排行榜")
    @GetMapping("/ctfLeaderboard")
    public Result<IPage<CtfLeaderBoardDto>> ctfLeaderboard(){
        log.info("CTF实验得分排行榜查询");
        IPage<CtfLeaderBoardDto> leaderboard = rangeService.ctfLeaderboard();
        return Result.success(200,"CTF实验得分排行榜查询成功",leaderboard);
    }


    /**
     *根据关键词查询靶场（条件查询）
     * @param page 页码
     * @param size 每页显示数
     * @param condition 条件
     * @return IPage<RangeDto>
     */
    @ApiOperation("44 关键词搜索CTF实验")
    @GetMapping("/queryRangeByCondition")
    public Result<IPage<RangeDto>> queryRangeByCondition(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1")
            @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size,
            @ApiParam(value = "关键词") @NotBlank(message = "关键字不能为空")
            @Length(max = 50,message = "关键字长度不能超过50") String condition
    ){
        log.info("根据关键词查询靶场:{}", condition);
        IPage<RangeDto> pages =  rangeService.queryRangeByCondition(page, size, condition);
        return Result.success(200, "根据关键词查询靶场成功", pages);
    }

    /**
     *获取靶场详情（根据ID查询靶场）
     * @param rangeId 靶场ID
     * @return RangeDto
     */
    @ApiOperation("46 获取实验（靶场）详情")
    @GetMapping("/findRangeById")
    public Result<RangeDto> findRangeById(@ApiParam(value = "靶场ID") @NotNull(message = "靶场ID不能为空")
                                              @Min(value = 1,message = "靶场ID不能为空") Integer rangeId){
        log.info("获取靶场详情:{}", rangeId);
        RangeDto pages =  rangeService.findRangeById(rangeId);
        return Result.success(200, "获取靶场详情成功", pages);
    }

    /**
     * 启动环境
     * @param rangeId 靶场ID
     * @return URL
     */
    @ApiOperation("48 启动实验环境")
    @PostMapping("/startup")
    public Result<String> startup(@ApiParam(value = "靶场ID") @RequestParam @NotNull(message = "靶场ID不能为空")
                                      @Min(value = 1,message = "靶场ID不能为空") Integer rangeId){
        User user = userService.assertUser();
        log.info("用户{}启动靶场{}", user.getId(), rangeId);
        String url = rangeService.startup(user, rangeId);
        return Result.success(200, "SUCCESS", url);
    }


    @ApiOperation("49 释放实验环境")
    @PostMapping("/release")
    public Result<Object> release(){
        User user = userService.assertUser();
        log.info("用户{}释放靶场", user.getId());
        experService.release(user.getId());
        return Result.success(200, "SUCCESS", null);
    }



    /**
     * 购买靶场
     * @param rangeId 靶场ID
     * @return RangeDto 靶场详情
     */
    @ApiOperation("55 购买实验")
    @PostMapping("/payRange")
    public Result<Object> payRange(@ApiParam(value = "靶场ID") @RequestParam @NotNull(message = "靶场ID不能为空") @Min(value = 1,message = "靶场ID不能为空")
                                       Integer rangeId,@ApiParam(value = "登录密码") @RequestParam
                                       @NotBlank(message = "密码不能为空") String password) throws Exception {
        log.info("开始购买靶场：{}",rangeId);
        rangeService.payRange(rangeId,password);
        return Result.success(200,"购买靶场成功",null);
    }

    /**
     * 下载靶场实验文件
     * @param rangeId 靶场ID
     */
    @ApiOperation("58 下载靶场实验文件")
    @GetMapping("/download")
    @ApiResponse(code = 200, message = "靶场文件url")
    public Result<String> downLoad(@ApiParam(value = "靶场ID") @NotNull(message = "靶场ID不能为空")
                                       @Min(value = 1,message = "靶场ID不能为空") Integer rangeId){
        Integer userId = JwtUtil.getUserIdFromReq();
        log.info("用户{}下载ctf实验{}文件", userId, rangeId);
        String url = rangeService.downloadExper(userId, rangeId);
        return Result.success(200, "SUCCESS", url);
    }

    //------------------------------------------------------------------------------------------------------
    //以下是安全实验页面相关接口

    @ApiOperation("32 查询所有安全实验 按最新")
    @GetMapping("getNewExperiment")
    public Result<IPage<GetRangeDto>> getNewExperiment(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1") @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size
    ){
        log.info("查询最新的安全实验{},{}",page,size);
        IPage<Range> rangeIPage = new Page<>(page == null ? 1 : page, size == null ? 10 : size);
        IPage<GetRangeDto> getRangeDtoIPage =  rangeService.getNewExperiment(rangeIPage);
        return Result.success(200,"查询成功",getRangeDtoIPage);
    }

    @ApiOperation("33 查询所有安全实验 按最热")
    @GetMapping("getHotExper")
    public Result<IPage<GetRangeDto>> getHotExper(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1") @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size
    ){
        log.info("查询最热的安全实验{},{}",page,size);
        IPage<Range> rangeIPage = new Page<>(page == null ? 1 : page, size == null ? 10 : size);
        IPage<GetRangeDto> getRangeDtoIPage = rangeService.getHotExper(rangeIPage);
        return Result.success(200,"查询成功",getRangeDtoIPage);
    }

    @ApiOperation("34 查询所有安全实验 按价格")
    @GetMapping("getExperOrderByMoney")
    public Result<IPage<GetRangeDto>> getExperOrderByMoney(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1") @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size,
            @ApiParam(value = "排序方式 [1升序,-1价格]") @NotNull(message = "排序方式不能为空") Integer sort
    ){
        log.info("价格降序升序查询安全实验{},{}",page,size);
        if (sort !=1 && sort != -1) throw new WebException(400,"参数错误");
        IPage<Range> rangeIPage = new Page<>(page == null ? 1 : page, size == null ? 10 : size);
        IPage<GetRangeDto> getRangeDtoIPage = rangeService.getExperOrderByMoney(rangeIPage,sort);
        return Result.success(200,"查询成功",getRangeDtoIPage);
    }

    @ApiOperation("35 按难度查询安全实验")
    @GetMapping("getExperByLevel")
    public Result<IPage<GetRangeDto>> getExperByLevel(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1") @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size,
            @ApiParam(value = "难度[low/middle/high]") @NotBlank(message = "难度不能为空")
            @Pattern(regexp = "low|middle|high",message = "难度参数传入错误") String level
    ){
        log.info("按难度查询安全实验靶场{},{},{}",page,size,level);
        if (!ArrayUtil.contains(RANGE_LEVEL, level)) throw new WebException(400,"难度传入错误");
        IPage<Range> rangeIPage = new Page<>(page == null ? 1 : page, size == null ? 10 : size);
        IPage<GetRangeDto> getRangeDtoIPage = rangeService.getExperByLevel(rangeIPage,level);
        return Result.success(200,"查询成功",getRangeDtoIPage);
    }


    @ApiOperation("36 按类别查询安全实验")
    @GetMapping("getExperByType")
    public Result<IPage<GetRangeDto>> getExperByType(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1") @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size,
            @ApiParam(value = "类别 [扫描类/文件类/SQL注入类/XSS类/CSRF类/SSRF类/代码执行/命令执行/文件上传类/文件包含类/反序列化类/Redis类/内网渗透类]")
            @NotBlank(message = "类别不能为空") @Pattern(regexp = "扫描类|文件类|SQL注入类|XSS类|CSRF类|SSRF类|代码执行|命令执行|文件上传类|文件包含类|反序列化类|Redis类|内网渗透类"
                    ,message = "类别参数传入错误") String rangeType
    ){
        log.info("按类别查询安全实验靶场{},{},{}",page,size,rangeType);
        if (!ArrayUtil.contains(RANGE_TYPE,rangeType)) {
            throw new WebException(400,"参数传入错误");
        }
        IPage<Range> rangeIPage = new Page<>(page,size);
        IPage<GetRangeDto> getRangeDtoIPage = rangeService.getExperByType(rangeIPage,rangeType);
        return Result.success(200,"查询成功",getRangeDtoIPage);
    }

    @ApiOperation("37 关键词搜索安全实验")
    @GetMapping("getExperByName")
    public Result<IPage<GetRangeDto>> getExperByName(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1") @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size,
            @ApiParam(value = "关键词") @NotBlank(message = "关键字不能为空") @Length(min = 1,max = 20,message = "关键字长度为1到20位") String name
    ){
        log.info("按关键词查询安全实验靶场{},{},{}",page,size,name);
        IPage<Range> rangeIPage = new Page<>(page == null ? 1 : page, size == null ? 10 : size);
        IPage<GetRangeDto> getRangeDtoIPage = rangeService.getExperByName(rangeIPage,name);
        return Result.success(200,"查询成功",getRangeDtoIPage);
    }

    @ApiOperation("38 安全实验积分排行榜")
    @GetMapping("experRankings")
    public Result<List<ExperRankingsDto>> experRankings(){
        log.info("查询安全实验排行");
        List<ExperRankingsDto> experRankingsDtos = rangeService.experRankings();
        return Result.success(200,"查询成功",experRankingsDtos);
    }

    //=================================================================================
    //以下是CTF实验页面相关接口

    @ApiOperation("39 查询所有CTF实验 按最新")
    @GetMapping("getNewCTF")
    public Result<IPage<GetRangeDto>> getNewCTF(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1") @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size
    ){
        log.info("查询最新的CTF实验{},{}",page,size);
        IPage<Range> rangeIPage = new Page<>(page == null ? 1 : page, size == null ? 10 : size);
        IPage<GetRangeDto> getRangeDtoIPage = rangeService.getNewCTF(rangeIPage);
        return Result.success(200,"查询成功",getRangeDtoIPage);
    }

    @ApiOperation("40 查询所有CTF实验 按最热")
    @GetMapping("getHotCTF")
    public Result<IPage<GetRangeDto>> getHotCTF(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1") @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size
    ){
        log.info("查询热度最高的CTF实验{},{}",page,size);
        IPage<Range> rangeIPage = new Page<>(page == null ? 1 : page, size == null ? 10 : size);
        IPage<GetRangeDto> getRangeDtoIPage = rangeService.getHotCTF(rangeIPage);
        return Result.success(200,"查询成功",getRangeDtoIPage);
    }

    @ApiOperation("41 查询所有CTF实验 按价格")
    @GetMapping("getCTFOrderByMoney")
    public Result<IPage<GetRangeDto>> getCTFOrderByMoney(
            @ApiParam(value = "第几页") @Min(value = 1,message = "当前页最小为1") @NotNull(message = "当前页不能为空") Integer page,
            @ApiParam(value = "每页条数") @NotNull(message = "展示数量不能为空") @Max(value = 50,message = "展示数量最大为50")
            @Min(value = 1,message = "展示数量最小为1") Integer size,
            @ApiParam(value = "排序方式[1升序,-1降序]") @NotNull(message = "排序方式不能为空") Integer sort
    ){
        log.info("价格降序升序查询CTF实验{},{}",page,size);
        if (sort != 1 && sort != -1) throw new WebException(400,"传入参数错误");
        IPage<Range> rangeIPage = new Page<>(page == null ? 1 : page, size == null ? 10 : size);
        IPage<GetRangeDto> getRangeDtoIPage = rangeService.getCTFOrderByMoney(rangeIPage,sort);
        return Result.success(200,"查询成功",getRangeDtoIPage);
    }
}
